using System.Security.Cryptography;
using FaceRecognitionDemo.Data;
using FaceRecognitionDemo.Models;
using Newtonsoft.Json;
using ViewFaceCore.Core;
using ViewFaceCore.Model;
using SkiaSharp;
using ViewFaceCore;

namespace FaceRecognitionDemo.Services
{
    public class FaceRecognitionService
    {
        private readonly FaceDatabase _database;
        private readonly FaceDetector _faceDetector;
        private readonly FaceRecognizer _faceRecognizer;

        private DomainService _domainService;

        public FaceRecognitionService(FaceDatabase database)
        {
            _database = database;
            _domainService = new DomainService(database);

            _faceDetector = new FaceDetector();
            _faceRecognizer = new FaceRecognizer();
        }

        private string ComputeFileHash(string filePath)
        {
            using var sha256 = SHA256.Create();
            using var stream = File.OpenRead(filePath);
            var hashBytes = sha256.ComputeHash(stream);
            return BitConverter.ToString(hashBytes).Replace("-", "").ToLower();
        }

        public async Task<Person> AddPerson(string name, string description = null)
        {
            var p = await _domainService.GetPerson(name);
            if (p != null) return p;

            var person = new Person {Name = name, Description = description};
            // _database.Persons.Add(person);
            // return await _database.SaveChangesAsync();
            return await _domainService.AddPerson(person);
        }

        static void FaceDetectorDemo(string imagePath)
        {
            using var bitmap = SKBitmap.Decode(imagePath);
            using FaceDetector faceDetector = new FaceDetector();
            FaceInfo[] infos = faceDetector.Detect(bitmap);
            Console.WriteLine($"识别到的人脸数量：{infos.Length} 个人脸信息：\n");
            Console.WriteLine($"No.\t人脸置信度\t位置信息");
            for (int i = 0; i < infos.Length; i++)
            {
                Console.WriteLine($"{i}\t{infos[i].Score:f8}\t{infos[i].Location}");
            }

            Console.WriteLine();
        }

        public static FaceInfo[] GetFaces(SKBitmap bitmap)
        {
            using FaceDetector faceDetector = new FaceDetector();
            FaceInfo[] faces = faceDetector.Detect(bitmap);
            return faces;
        }

        /// <summary>
        /// 标记人脸位置
        /// </summary>
        /// <param name="bitmap"></param>
        /// <param name="faceInfo"></param>
        /// <returns></returns>
        public float[] GetFaceFeature(SKBitmap bitmap, FaceInfo faceInfo)
        {
            using FaceLandmarker faceMark = new FaceLandmarker();
            var markPoints = faceMark.Mark(bitmap, faceInfo);

            var infos = _faceRecognizer.Extract(bitmap, markPoints);
            return infos;
        }



        private float CalculateSimilarity(float[] feature1, float[] feature2)
        {
            return _faceRecognizer.Compare(feature1, feature2);
        }

        public async Task AddFaceToPerson(int personId, string imagePath, bool isPrimary = false)
        {
            var hash = ComputeFileHash(imagePath);

            if (_database.Faces.Any(f => f.ImageHash == hash))
                throw new Exception("相同的图片已存在");
            
            using var bitmap = SKBitmap.Decode(imagePath);
            FaceInfo[] faces = GetFaces(bitmap);
            
            if (faces.Length == 0)
                throw new Exception("未检测到人脸");
            if (faces.Length > 1)
                throw new Exception("检测到多张人脸");
            
            FaceInfo faceInfo = faces.First();
            var feature = GetFaceFeature(bitmap, faceInfo);

            var face = new Face
            {
                PersonId = personId,
                Feature = feature,
                ImageHash = hash,
                IsPrimary = isPrimary,
                SampleImagePath = imagePath,
                Location = JsonConvert.SerializeObject(faceInfo.Location),
                Score = faceInfo.Score
            };

            _database.Faces.Add(face);
            await _database.SaveChangesAsync();
        }

        public async Task<List<FaceComparison>> CompareFaces(string name, string sourceImagePath)
        {
            var person = await _domainService.GetPerson(name);
            if (person == null)
            {
                throw new Exception($"未找到该用户:{name}" );
            }

            var results = new List<FaceComparison>();
            var allFaces = _database.Faces.Where(f => f.PersonId == person.Id).ToList();

            var sourceHash = ComputeFileHash(sourceImagePath);

            using var bitmap = SKBitmap.Decode(sourceImagePath);
            using FaceDetector faceDetector = new FaceDetector();
            FaceInfo[] sourceFaces = faceDetector.Detect(bitmap);
            foreach (var sourceFace in sourceFaces)
            {
                using FaceLandmarker faceMark = new FaceLandmarker();
                var markPoints = faceMark.Mark(bitmap, sourceFace);
                var sourceFeature = _faceRecognizer.Extract(bitmap, markPoints);

                foreach (var face in allFaces)
                {
                    //先检测此图片与此人脸样本，是否已经比对过。
                    if (_database.FaceComparisons.Any(fc =>
                            fc.SourceImageHash == sourceHash && fc.ComparedFaceId == face.Id))
                        continue;

                    //计算人脸相似度
                    var score = CalculateSimilarity(sourceFeature, face.Feature);
                    // Console.WriteLine($"{sourceImagePath} 与 {face.Id} 的人脸相似度为：{score:f4}");

                    //记录日志
                    var comparison = new FaceComparison
                    {
                        SourceImagePath = sourceImagePath,
                        SourceImageHash = sourceHash,
                        ComparisonTime = DateTime.Now,
                        PersonId = face.PersonId,
                        ComparedFaceId = face.Id,
                        ComparedFaceImagePath = face.SampleImagePath,
                        SimilarityScore = score
                    };

                    results.Add(comparison);
                    _database.FaceComparisons.Add(comparison);
                }
            }

            await _database.SaveChangesAsync();
            return results;
        }
    }
}